导航: 嵌套导航图和 <include> | MAD Skills
这是第二个关于导航 (Navigation) 的 MAD Skills 系列,本文是导航组件系列的第三篇文章,如果您想回顾过去发布的内容,请参考下面链接查看:
△ 导航: 嵌套导航图和 <include>
概述
在本系列之前的文章中,我们增加了咖啡记录功能,使用导航 UI 提高了用户体验,并且实现了有条件导航。
在本文中,我们将了解如何通过使用嵌套图管理导航图,并且使用 include 标签来引入其他图。这就需要我们将应用模块化,并且了解导航如何在模块间实现操作。
那么,接下来,让我们打开 Android Studio 开始学习如何在模块上使用导航吧。
嵌套导航图
我们从导航图开始。嵌套图允许您在父导航图中将一系列目的地页面分组。
我们看一眼导航图,coffeeList 和 coffeeEntryDialog 目的地页面非常适合转换为嵌套图。要达成这个目的,我这里长按 shift 并且同时选择 "Move to Nested Graph" (移动到嵌套图):
现在我们回到代码界面,您可以看到嵌套图仅仅是根图中的新导航图:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
app:startDestination="@id/donutList">
<fragment
android:id="@+id/donutList"
android:name="com.android.samples.donuttracker.donut.DonutList"
android:label="@string/donut_list" >
<action
android:id="@+id/action_donutList_to_donutEntryDialogFragment"
app:destination="@id/donutEntryDialogFragment" />
<action
android:id="@+id/action_donutList_to_selectionFragment"
app:destination="@id/selectionFragment" />
</fragment>
<dialog
android:id="@+id/donutEntryDialogFragment"
android:name="com.android.samples.donuttracker.donut.DonutEntryDialogFragment"
android:label="DonutEntryDialogFragment">
<deepLink app:uri="myapp://navdonutcreator.com/donutcreator" />
<argument
android:name="itemId"
app:argType="long"
android:defaultValue="-1L" />
</dialog>
<fragment
android:id="@+id/selectionFragment"
android:name="com.android.samples.donuttracker.setup.SelectionFragment"
android:label="@string/settings"
tools:layout="@layout/fragment_selection" >
<action
android:id="@+id/action_selectionFragment_to_donutList"
app:destination="@id/donutList" />
</fragment>
<navigation
android:id="@+id/coffeeGraph"
app:startDestination="@id/coffeeList">
<fragment
android:id="@+id/coffeeList"
android:name="com.android.samples.donuttracker.coffee.CoffeeList"
android:label="@string/coffee_list">
<action
android:id="@+id/action_coffeeList_to_coffeeEntryDialogFragment"
app:destination="@id/coffeeEntryDialogFragment" />
</fragment>
<dialog
android:id="@+id/coffeeEntryDialogFragment"
android:name="com.android.samples.donuttracker.coffee.CoffeeEntryDialogFragment"
android:label="CoffeeEntryDialogFragment">
<argument
android:name="itemId"
android:defaultValue="-1L"
app:argType="long" />
</dialog>
</navigation>
</navigation>
<navigation
android:id="@+id/coffeeGraph"
app:startDestination="@id/coffeeList">
如果您双击嵌套图,就可以发现嵌套的目的地页面和它们之间的操作。
Include 标签
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coffeeGraph"
app:startDestination="@id/coffeeList">
<fragment
android:id="@+id/coffeeList"
android:name="com.android.samples.donuttracker.coffee.CoffeeList"
android:label="@string/coffee_list">
<action
android:id="@+id/action_coffeeList_to_coffeeEntryDialogFragment"
app:destination="@id/coffeeEntryDialogFragment" />
</fragment>
<dialog
android:id="@+id/coffeeEntryDialogFragment"
android:name="com.android.samples.donuttracker.coffee.CoffeeEntryDialogFragment"
android:label="CoffeeEntryDialogFragment">
<argument
android:name="itemId"
android:defaultValue="-1L"
app:argType="long" />
</dialog>
</navigation>
我可以通过 include 标签将新的图嵌套到其他文件中。虽然使用 include 标签在功能上与使用嵌套图相同,但您还可以使用其他项目模块或者库项目的图。
<include app:graph="@navigation/coffee_graph"/>
和嵌套图相类似,引用的图不会暴露目的地页面的列表,也就是说我需要更新菜单 id 来指向 coffeeList。
<item
android:id="@id/coffeeGraph"
android:icon="@drawable/coffee_cup"
android:title="@string/coffee_name" />
这里我更新了菜单以使用引用图的 id。由于 CoffeeList 是所引用图的起始页面,所以我可以使用图 id 来导航到这个图。如果您现在试着运行应用,所有的功能会和之前一样。
现在咖啡记录的导航图已经实现分离,我们可以对应用进行模块化处理,顺便可以看一下在模块之间导航的效果如何。
如果您希望同步操作,可以检查代码,里面包含了到目前为止我所做的全部修改。我创建了两个新的模块: core 和 coffee。我将所有常用的类迁移到 core 模块中,比如 Donut、Coffee、DAO、Database 以及其他常见资源。
代码
https://github.com/google-developer-training/android-demos/tree/starter/DonutTracker/NestedGraphs_Include
接下来,我将所有在咖啡记录中用到的 fragment、viewModel 和 adapter 类迁移到 coffee 模块中。在咖啡记录中用到的布局和其他资源也迁移到这里,包括 coffee_graph。
coffee 模块依赖 core 模块:
dependencies {
implementation project(":core")
//...
}
最后,在 app 模块中,添加 coffee 和 core 作为 app 模块的依赖:
dependencies {
implementation project(":coffee")
implementation project(":core")
//..
}
请注意这里的导航图没有任何变化,它不受这些修改的影响:
△ 导航图没有发生变化
现在如果运行应用,所有的功能一如往常,只不过内部使用了模块。您可以查看最终的代码:
通过上述修改,我将咖啡记录模块和与它相关的导航流从应用中分离了出来,也就意味着咖啡记录模块可以独立于甜甜圈记录应用使用。
总结
推荐阅读